package com.example.d6_homework.homework07.classloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * 自定义热更新的类加载器
 * 该类加载器用于实现类的热更新,能够动态地从指定路径加载类的字节码
 */
public class HotClassLoader extends ClassLoader {
    private final String packagePath;

    /**
     * 构造方法
     * 初始化类加载器,并接收一个包路径作为参数,用于后续加载类字节码文件
     *
     * @param packagePath 类包的路径,用于定位类字节码文件
     */
    public HotClassLoader(String packagePath) {
        this.packagePath = packagePath.replace('.', '/');
    }

    /**
     * 加载类
     * 重写loadClass方法,实现自定义类加载逻辑,首先尝试从已加载类中获取,
     * 若未找到则尝试使用父加载器加载,再未找到则自行加载
     *
     * @param name    类名
     * @param resolve 是否解析类
     * @return 加载的类
     * @throws ClassNotFoundException 如果类未找到
     */
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 检查是否已经加载了该类
        Class<?> clazz = findLoadedClass(name);
        if (clazz == null) {
            // 尝试从父加载器加载
            try {
                clazz = getParent().loadClass(name);
            } catch (ClassNotFoundException e) {
                // 如果父加载器没有找到,则尝试自己加载
                if (name.startsWith("com.example.d6_homework.homework07.domain")) {
                    clazz = findClass(name);
                } else {
                    throw e;
                }
            }
        }
        if (resolve) {
            resolveClass(clazz);
        }
        return clazz;
    }

    /**
     * 查找类
     * 重写findClass方法,根据类名找到对应的字节码文件,并将其转换为字节数组返回
     *
     * @param name 类名
     * @return 加载的类
     * @throws ClassNotFoundException 如果类未找到
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassData(name);
        if (classData == null) {
            throw new ClassNotFoundException();
        } else {
            return defineClass(name, classData, 0, classData.length);
        }
    }

    /**
     * 加载类数据
     * 根据类名找到对应的字节码文件,并将其内容读取到字节数组中
     *
     * @param className 类名
     * @return 类的字节码数据
     */
    private byte[] loadClassData(String className) {
        String path = "src/" + packagePath + "/" + className.substring(className.lastIndexOf('.') + 1) + ".class";
        File file = new File(path);
        try (FileInputStream fis = new FileInputStream(file);
             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int bytesRead = -1;
            while ((bytesRead = fis.read(buffer, 0, bufferSize)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
